interface Array<T> {
    getNoRepeatsUseRepeatTest(isRepeated:(item1:T,item2:T)=> boolean):Array<T>;
    findIndex(test:(item:T,index:number,arr:Array<T>)=>boolean):number;
}

Array.prototype.getNoRepeatsUseRepeatTest = function <T>(isRepeated:(item1:T,item2:T)=> boolean):Array<T> {
  
    return this.filter(function (currentItem, index, arr) {
      return index === arr.findIndex(function (findItem) {
        return isRepeated(findItem, currentItem);
      });
    });

}




/**
 * 重复选项的类型
 */
type ReOptions = {
    title?:boolean;
    url?:boolean;
};

function isReOptions(target:any):target is ReOptions {
    return target && (<Object>target).constructor == Object ;
}

/**
 * 搜索结果的类型
 */
type SubElementArray = Array<Element> & {root:Element;}


/**
 * 查找根元素
 */
function getRootElement(selector:string):Element;
function getRootElement(ele:Element):Element;
function getRootElement(doc:Document):Element;
function getRootElement(target?:Document|Element|string):Element;
function getRootElement(target?:Document|Element|string):Element {

    let rootEle:Element;
    if  (typeof target == "string"){
        rootEle  = document.querySelector(target)!;
    }else if(target instanceof Element){
        rootEle = target;
    }else if(target instanceof Document){
        rootEle = target.children[0];
    }else{
        rootEle = document.children[0];
    }

    return rootEle;
}





/**
 * 搜索重复的元素
 */
function searchDuplicate(options:ReOptions):SubElementArray;
function searchDuplicate(selector:string,options?:ReOptions):SubElementArray;
function searchDuplicate(ele:Element,options?:ReOptions):SubElementArray;
function searchDuplicate(doc:Document,options?:ReOptions):SubElementArray;
function searchDuplicate(target?:Document|Element|string|ReOptions,options?:ReOptions):SubElementArray;
function searchDuplicate(target?:Document|Element|string|ReOptions,options?:ReOptions):SubElementArray {

    let rootEle:Element;
    if (isReOptions(target)){
        rootEle = document.children[0];
        options = target;
    } else {
        rootEle = getRootElement(target);
    }

    let {title,url} = options || {url:true,title:false};


    let bmColl = rootEle.getElementsByTagName("A");
    let bmArr = <HTMLAnchorElement[]>Array.prototype.slice.apply(bmColl);

    switch(true){
        case title && url :{
            var repeatTest:(bm1:HTMLAnchorElement,bm2:HTMLAnchorElement)=>boolean = function(bm1,bm2){
                return bm1.href == bm2.href && bm1.textContent == bm2.textContent ;
            };
            break;
        }

        case title && !url :{
            repeatTest = function(bm1,bm2){
                return bm1.textContent == bm2.textContent ;
            };
            break;
        }
        default:{
            repeatTest = function(bm1,bm2){
                return bm1.href == bm2.href ;
            };
        }
        
    }



    let dupBmArr =  <SubElementArray><Object>bmArr.filter(function (tarBm) {
        let reTarBmArr = bmArr.filter(function (compBm) {
            return repeatTest(tarBm, compBm);
        });
        return reTarBmArr.length > 1;
    });

    dupBmArr.root = rootEle;
    return dupBmArr;
}


type TitleValueObj = {
    [prop:string]:string
};
function getTitleUrlObjOfBookmarkElements(bmEleArr:Element[]):TitleValueObj{
    let titVlaObj:TitleValueObj = {};

    titVlaObj = bmEleArr.reduce(function(obj,ele){
        let title = ele.textContent!;
        let url = (<HTMLAnchorElement>ele).href;
        obj[title] = url;
        return obj;
    },titVlaObj);

    return titVlaObj;
}




/**
 * 获取书签元素的路径元素数组
 * @param ele 
 * @param rootEle 
 */
function getBookmarkPathElements(ele:Element,rootEle:Element):Element[]|null{
    let pathEleArr:Element[] = [ele];

    let isSame = ele.isSameNode(rootEle);
    let parent = ele.parentElement;

    while (parent && !isSame){
        if (parent.tagName == "DL") {
            let pathEle = parent.previousElementSibling;
            if(pathEle && pathEle.hasAttribute("FOLDED")){
                pathEleArr.unshift(pathEle);
            }
        }

        parent = parent.parentElement;
        if (parent){
            isSame = parent.isSameNode(rootEle);
        }
    }

    return isSame ? pathEleArr : null;
}


/**
 * 获取书签元素的路径名数组
 * @param ele 
 * @param rootEle 
 */
function getBookmarkPaths(ele:Element,rootEle:Element):string[]|null{
    let pathEleArr = getBookmarkPathElements(ele,rootEle);
    if (pathEleArr){
        var pathArr = <string[]|null>pathEleArr.map(function(pathEle){
            return pathEle.textContent;
        });
    } else {
        pathArr = null;
    }

    return pathArr
}


/**
 * 获取书签元素列表对应的路径元素数组的数组
 * @param subEleArr 
 */
function getBookmarkPathElementsList(subEleArr:SubElementArray):Element[][]{
    let rootEle = subEleArr.root;
    let pathArr:Element[][] = [];
     subEleArr.forEach(function(ele){
        let eleArr = getBookmarkPathElements(ele,rootEle);
        if(eleArr){
            pathArr.push(eleArr)
        }
    });

    return pathArr;
}




type PathTree = string[] & {
    [propName:string]:any;
}

function getPathTreeOfBookmarkPathElementsList(pathElesList:Element[][]):PathTree{

    let pathTree:PathTree = [];

    pathElesList.forEach(function(pathEles){

        pathEles.reduce(function(parent,pathE){
            let pathName = pathE.textContent || "无名字";
        if (pathE.tagName == "A"){
            parent.push(pathName);
            return parent;
        }else{
            return parent[pathName] = parent[pathName] || [];
        }

        },pathTree);

    });

    return pathTree;
}





/**
 * 搜索重复的书签并格式化成对象输出
 * @param options 
 */
function getPathTreeOfDuplicate(options:ReOptions):PathTree;
function getPathTreeOfDuplicate(selector:string,options?:ReOptions):PathTree;
function getPathTreeOfDuplicate(ele:Element,options?:ReOptions):PathTree;
function getPathTreeOfDuplicate(doc:Document,options?:ReOptions):PathTree;
function getPathTreeOfDuplicate(target?:Document|Element|string|ReOptions,options?:ReOptions):PathTree;
function getPathTreeOfDuplicate(target?:Document|Element|string|ReOptions,options?:ReOptions):PathTree{
    let dupBmArr = searchDuplicate(target,options);
    let pathEleList = getBookmarkPathElementsList(dupBmArr);
    return getPathTreeOfBookmarkPathElementsList(pathEleList);
}





/**
 * 搜索重复的书签并格式化成{title:url}的格式
 * @param options 
 */
function getTitleValueObjOfDuplicate(options:ReOptions):TitleValueObj;
function getTitleValueObjOfDuplicate(selector:string,options?:ReOptions):TitleValueObj;
function getTitleValueObjOfDuplicate(ele:Element,options?:ReOptions):TitleValueObj;
function getTitleValueObjOfDuplicate(doc:Document,options?:ReOptions):TitleValueObj;
function getTitleValueObjOfDuplicate(target?:Document|Element|string|ReOptions,options?:ReOptions):TitleValueObj;
function getTitleValueObjOfDuplicate(target?:Document|Element|string|ReOptions,options?:ReOptions):TitleValueObj{
    let dupBmArr = searchDuplicate(target,options);
    let noReDupBmArr = dupBmArr.getNoRepeatsUseRepeatTest(function(ele1,ele2){
        return (<HTMLAnchorElement>ele1).href == (<HTMLAnchorElement>ele2).href;
    });
    return getTitleUrlObjOfBookmarkElements(noReDupBmArr);
}